/**
 * Copyright (c) 2017-2018, zengjintao (1913188966@qq.com).
 * <p>
 * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.gnu.org/licenses/lgpl-3.0.txt
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jfast.framework.web.orm;


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.beanutils.PropertyUtils;
import com.jfast.framework.exception.DataSourceException;
import com.jfast.framework.web.orm.config.DataSourceManager;
import com.jfast.framework.web.orm.config.DbConfig;
import com.jfast.framework.web.orm.config.TableMapping;
import com.jfast.framework.web.orm.dialect.Dialect;

public class JdbcTemplate {

	static final Object[] NULL_PARA_ARRAY = new Object[0];
	
	private static final ModelBuilder modelBuilder = ModelBuilder.me();
	
	public static <T> List<T> findAll(String sql,Class<T> cla,Object...  paras){
		String configName = getConfigName(cla);
		DbConfig dbConfig = DataSourceManager.getDbConfig(configName);
		return find(dbConfig, sql, cla, paras);
	}
	
	public static <T> List<T> findAll(String sql,Class<T> cla){
		String configName = getConfigName(cla);
		DbConfig dbConfig = DataSourceManager.getDbConfig(configName);
		return find(dbConfig, sql,cla);
	}
	
	@SuppressWarnings({ "rawtypes"})
	protected static String getConfigName(Class cla){
		TableMapping tableMapping = DataSourceManager.getTableMappingMap(cla.getPackage().getName());
		return tableMapping.geConfigName();
	}
	
	private static <T> List<T> find(DbConfig dbConfig, String sql,Class<T> cla){
		Connection connection = null;
		try {
			connection = dbConfig.getConnection();
			PreparedStatement preparedStatement = connection.prepareStatement(sql);
			ResultSet resultSet = preparedStatement.executeQuery();
			List<T> modelList = modelBuilder.builderModelList(resultSet,cla);
			return modelList;
		} catch (SQLException e) {
			throw new DataSourceException(e);
		}finally{
			dbConfig.close(connection);
		}
	}

	public static <T> T findOne(String sql,Class<T> cla){
		List<T>  result = findAll(sql, cla,NULL_PARA_ARRAY);
		return result.size()> 0 ? result.get(0) : null;
	}
	
	public static <T> T findOne(String sql,Class<T> cla,Object... paras) {
		List<T> result = findAll(sql, cla,paras);
		return result.size()> 0 ? result.get(0) : null;
	}
	
	private static <T> List<T> find(DbConfig dbConfig, String sql,Class<T> cla,Object... paras){
		Connection connection = null;
		try {
			connection = dbConfig.getConnection();
			PreparedStatement preparedStatement = connection.prepareStatement(sql);
			modelBuilder.fillStatement(preparedStatement,paras);
			ResultSet resultSet = preparedStatement.executeQuery();
			List<T> modelList;
			modelList = modelBuilder.builderModelList(resultSet,cla);
			return modelList;
		} catch (Exception e) {
			throw new DataSourceException(e);
		}finally {
			dbConfig.close(connection);
		}
	}
	
	public static boolean update(Object object){
		String configName = getConfigName(object.getClass());
		DbConfig dbConfig = DataSourceManager.getDbConfig(configName);
		return update(dbConfig,object);
	}

	private static boolean update(DbConfig dbConfig, Object object) {
		String configName = dbConfig.getDataSourceFactory().getConfigName();
		TableMapping tableMapping = DataSourceManager.getTableMappingMap(configName);
		TableInfo tableInfo = tableMapping.getTableInfo(object.getClass());
		Map<String,Object> columnAttr = new HashMap<>();
		for (Entry<String, String> entry: tableInfo.getColumnMapping().entrySet()) {
			try {
				columnAttr.put(entry.getValue(), PropertyUtils.getProperty(object, entry.getKey()));
			} catch (Exception e) {
				throw new DataSourceException(e);
			}
		}
		Dialect dialect = DataSourceManager.getDialect(dbConfig);
		List<Object> paras = new ArrayList<Object>();
		StringBuilder sql = new StringBuilder(); 
		dialect.forModelSave(sql, columnAttr, tableInfo.getTableName(),paras);
		Connection connection = null;
		try {
			connection = dbConfig.getConnection();
			return update(sql.toString(),tableInfo.getPrimaryKeys(),connection,paras) >= 1;
		} catch (Exception e) {
			throw new DataSourceException(e);
		}finally{
			dbConfig.close(connection);
		}
	}

	private static int update(String sql,String[] primaryKeys,Connection connection, List<Object> paras) throws SQLException {
		PreparedStatement statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
		modelBuilder.fillStatement(statement,paras.toArray());
		return statement.executeUpdate();
	}
	
	
	public <M> Page<M> paginate(int pageNumber, int pageSize, String select, String sqlExceptSelect,Class<M> cla) {
		return doPaginate(pageNumber, pageSize, null, select, sqlExceptSelect, NULL_PARA_ARRAY);
	}
	
	private <M> Page<M> doPaginate(int pageNumber, int pageSize, Object object, String select,
			String sqlExceptSelect, Object[] nullParaArray) {
		return null;
	}
}
